﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Maestro_Proveedores.clases.almacen
{
    class Croston : ModeloAbstracto
    {

        private double alfaOptimo;
        private double NoTotal;
        private double Z0Total;
        ArrayList resultadosN;

        //-------------------------CONSTRUCTOR DE CLASE
        public Croston()
        {
            this.nombreModelo = "Croston";
            this.resultadosN = new System.Collections.ArrayList();
            resNideal = new Resultado();
        }

        //------------------------ INICIO METODOS SOBREESCRITOS

        public override Resultado obtenerResultado()
        {
            return this.resNideal;
        }

        public override void pronosticoSemanaN()
        {
            semanaPronostico = registros.Count;
            RegistroCRO regAnterior = (RegistroCRO)registros[semanaPronostico - 1];
            //calculo del pronostico
            resNideal.pronosticoN1 = regAnterior.Zt / regAnterior.Nt;
        }

        public override void calcularModelo(System.Collections.ArrayList registrosF)
        {
            //se instancia un arreglo de registros para CROSTON
            this.registros = newListRegistrosCRO(registrosF);
            // se evalua que existan mas de 52 datos..
            if (registros.Count >= 53)
            {
             // Calculo de los N0 para cada Registro hasta la semana 45
             ArrayList calculoNo = calculoN0(this.registros);
             // caluclo de No Total 
             this.NoTotal = calcularN0Total(calculoNo);
             // calculo de Z0 Total
             this.Z0Total = calcularZ0Total(calculoNo);
             //calculo parcial desde la semana 46 en adelante

             //ArrayList pronosticoInicial = pronosticoParcial(calculoNo, alfaInicio);
             ////calculan los resultados ET, ETabsoluto, Etcuadrado
             //resNideal = calcularResultados(pronosticoInicial);
             
             for (double a = 0.001; a < 1; a = a + 0.001)
             {
                 ////calculo de la primera corrida
                 ArrayList pronosticoInicial = pronosticoParcial(calculoNo, a);
                 ////calculan los resultados ET, ETabsoluto, Etcuadrado
                 Resultado resultadoParcial = new Resultado();
                 resultadoParcial = calcularResultados(pronosticoInicial);
                 resultadoParcial.AlfaOptimo = a;
                 this.resultadosN.Add(resultadoParcial);
             }

             this.resNideal = CalcAlfaOptimo(resultadosN);

             //se asigna el alfa optimo
             this.alfaOptimo = resNideal.AlfaOptimo;

             //Pronostico con el alfa ideal
             ArrayList pronosticoFinal = pronosticoParcial(registros,alfaOptimo);
             registros = pronosticoFinal;
             //calculan los resultados ET, ETabsoluto, Etcuadrado
             resNideal = calcularResultados(pronosticoFinal);
             resNideal.RegistrosFinal = pronosticoFinal;
             //this.mostrarArreglo("Croston-Alfa-"+alfaOptimo);
             // se calcula el pronostico para la siguiente semana (N+1) se al macena en el resNIDEal
             pronosticoSemanaN();
            }
            else
            {
                // el archivo no tiene minimo 53 semanas
                System.Windows.Forms.MessageBox.Show("Debe cargar minimo 53 Semanas");
            } 

        }

        //------------------------ FIN METODOS SOBREESCRITOS
        //Funcion que realiza el calculo de pronostico a partir de un lista de registros y un alfa definicio
        private ArrayList pronosticoParcial(ArrayList registros, double alfa)
        {
            // se realiza una copia de la lista original, para cada calculo de N
            ArrayList registrosClon = new System.Collections.ArrayList();
            registrosClon = copiarArrayListCRO(registros);
            RegistroCRO regAnterior = new RegistroCRO();
            foreach (RegistroCRO regCro in registrosClon)
            {
                if (regCro.Semana == 45)
                {
                    regCro.Nt = this.NoTotal;
                    regCro.Zt = this.Z0Total;
                    regCro.ContadorNt=0;
                    regAnterior = regCro;
                }
                else
                if (regCro.Semana > 45)
                {
                    //Inicializa el contador de Nt en 1 para la semana 53
                    if(regCro.Semana == 46)
                     regCro.ContadorNt=1;

                    //se calcula el contador
                    if (regAnterior.Demanda > 0)
                        regCro.ContadorNt = 1;
                    else
                        regCro.ContadorNt = regAnterior.ContadorNt + 1;

                    //se evalua si la demanda es igual a cero y se calcula el NT el ZT 
                    if (regCro.Demanda == 0)
                    {
                        regCro.Nt = regAnterior.Nt;
                        regCro.Zt = regAnterior.Zt;
                    } 
                    else{
                        regCro.Nt = alfa * regCro.ContadorNt + (1 - alfa) * regAnterior.Nt;
                        regCro.Zt = alfa * regCro.Demanda + (1 - alfa) * regAnterior.Zt;
                    }

                    //calculo del pronostico
                    regCro.Pronostico = regAnterior.Zt / regAnterior.Nt;
                    //se calcula el et
                    regCro.Et = regCro.Demanda - regCro.Pronostico;
                    //se calcula el etabsoluto
                    regCro.Etabsoluto = Math.Abs(regCro.Et);
                    //se calcula el etcuadrado
                    regCro.Etcuadrado = Math.Pow(regCro.Et, 2);
                    //se calcula el APE
                    regCro.Ape = 100 * (Math.Abs(regCro.Et / regCro.Demanda));
                    //se calcula el APE1
                    regCro.Ape1 = 100 * (Math.Abs(regCro.Et / regCro.Pronostico));
                    //se actualiza el registro anterior para el proximo pronostico
                    regAnterior = regCro;
                }
            }
            return registrosClon;
        }

        private double optimizarAlfa(Resultado resultados)
        {
            this.alfaOptimo = 0.01;
            return alfaOptimo;
        }

        private Resultado calcularResultados(ArrayList registros)
        {
            Resultado resultado = new Resultado();

            double sumatoriaMAD = 0;
            double sumatoriaECM = 0;
            double sumatoriaMAPE = 0;
            double sumatoriaMAPE1 = 0;
            double count = 0;

            foreach (RegistroCRO reg in registros)
            {
                if (reg.Pronostico != 0)
                {
                    sumatoriaMAD += reg.Etabsoluto;
                    sumatoriaECM += reg.Etcuadrado;
                    sumatoriaMAPE += reg.Ape;
                    sumatoriaMAPE1 += reg.Ape1;
                    count++;
                }
            }

            resultado.ECM = sumatoriaECM / count;
            resultado.MAD = sumatoriaMAD / count;
            resultado.MAPE = sumatoriaMAPE / count;
            resultado.MAPE1 = sumatoriaMAPE1 / count;

            return resultado;
        }

        // funcion para calcular el promedio de la semana n
        private double calcularZ0Total(ArrayList calculoNo)
        {
            double sum = 0;
            double cont0 = 0;
            foreach (Registro regCro in registros)
            {
                if (regCro.Semana <= 45)
                {
                    if (regCro.Demanda != 0)
                        cont0++;
                    sum += regCro.Demanda;
                }
                else
                    break;
            }

            try
            {
                return (sum / cont0);
            }
            catch (System.DivideByZeroException ex)
            {
                System.Console.WriteLine("Division by zero attempted!"+ ex.Message);
                return 0;
            }
        }
                
        //metodo para calcular el No General Sumatoria de Todos lo No / cuenta de  No diferentes de 0
        private double calcularN0Total(ArrayList calculoNo)
        {
            double cont0 = 0;
            double sumatoria = 0;
                 
            foreach (RegistroCRO regCro in calculoNo) {
                if (regCro.Semana < 46)
                {  
                    if (regCro.N0 != 0)
                        cont0++;

                    sumatoria += regCro.N0;
                }
                else
                    break;
            }
            try
            {
                return (sumatoria / cont0);
            }
            catch (System.DivideByZeroException ex)
            {
                System.Console.WriteLine("Division by zero attempted!" + ex.Message);
                return 0;
            }
        }

        private ArrayList calculoN0(ArrayList registros)
        {
            // se realiza una copia de la lista original, para cada calculo de N
            ArrayList registrosClon = new System.Collections.ArrayList();
            registrosClon = copiarArrayListCRO(registros);

            RegistroCRO regCroAnt=new RegistroCRO();

            foreach (RegistroCRO regCro in registrosClon) {
                //se limita el calculo para las primeras 45 semanas

                if (regCro.Semana < 46)
                {
                    //Para la semana 1 se asignan valores iniciales
                    if (regCro.Semana == 1)
                    {
                        regCro.N0 = 0;
                        regCro.N0Aux = 1;
                        regCroAnt = regCro;
                    }
                    // Para cada registro se calculan los valores para N0
                    else
                    {
                        if (regCro.Demanda == 0)
                        {
                            regCro.N0 = 0;
                            regCro.N0Aux = regCroAnt.N0Aux + 1;
                        }
                        else
                        {
                            regCro.N0 = regCroAnt.N0Aux;
                            regCro.N0Aux = 1;
                        }
                        regCroAnt = regCro;
                    }
                }
                else
                    break;
            }

            return registrosClon;
        }
        
        private System.Collections.ArrayList newListRegistrosCRO(System.Collections.ArrayList registros)
        {
            System.Collections.ArrayList registrosClon = new System.Collections.ArrayList();

            foreach (Registro reg in registros)
            {
                RegistroCRO regTemp = new RegistroCRO(reg);
                registrosClon.Add(regTemp);
            }

            return registrosClon;
        }

        public void mostrarArreglo(String nombre)
        {
            //Gestion_Inventario_Proveedores.clases.herramientas.FileManager file = new Gestion_Inventario_Proveedores.clases.herramientas.FileManager();
            //file.generarArchivoCRO(registros, nombre);
        }


    }
}
